﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Xtensible.Extensions.Mapping
{
    public class Helper
    {
        /// <summary>
        ///   Gets properties from type
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <returns></returns>
        internal static IEnumerable<PropertyInfo> GetProperties<T>()
        {
            return typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => p.CanWrite);
        }

        /// <summary>
        ///   Determines whether the specified property name is excluded.
        /// </summary>
        /// <param name = "propertyName">Name of the property.</param>
        /// <param name = "exclusions">The exclusions.</param>
        /// <returns>
        ///   <c>true</c> if the specified property name is excluded; otherwise, <c>false</c>.
        /// </returns>
        internal static bool ContainsExclusion(string propertyName, Exclusions exclusions)
        {
            //check for wildcard
            if (exclusions.Any(s => s.Contains("*")))
            {
                foreach (var exclusion in exclusions)
                {
                    var ex = exclusion.Replace("*", ".*");
                    var regex = new Regex(ex);
                    if (regex.IsMatch(propertyName))
                    {
                        return true;
                    }
                }
            }
            else
            {
                return exclusions.Contains(propertyName);
            }
            return false;
        }

        /// <summary>
        ///   Gets the source property
        /// </summary>
        internal static PropertyInfo GetSourceProperty(Aliases aliases, PropertyInfo targetProperty,
                                                       Exclusions exclusions, Type sourceType,
                                                       PropertyInfo sourceProperty)
        {
            if (aliases != null && aliases.Keys.Contains(targetProperty.Name))
            {
                //Excluded properties should not be mapped... even if aliased
                if (!(exclusions != null && ContainsExclusion(aliases[targetProperty.Name], exclusions)))
                    sourceProperty = sourceType.GetProperty(aliases[targetProperty.Name]);
            }
            else
            {
                // If target property is already aliased then do not match on source property of the same name... this will cause a double mapping
                if (aliases == null || (!aliases.Values.Contains(targetProperty.Name)))
                {
                    //do not map excluded properties
                    if (!(exclusions != null && ContainsExclusion(targetProperty.Name, exclusions)))
                    {
                        sourceProperty = sourceType.GetProperty(targetProperty.Name);
                    }
                }
            }
            return sourceProperty;
        }

        internal static string GetPropertyNameFromExpression<T>(Expression<Func<T, object>> exp)
        {
            string name = "";
           
            MemberExpression body = exp.Body as MemberExpression;
            if (body == null)
            {
                UnaryExpression ubody = (UnaryExpression)exp.Body;
                body = ubody.Operand as MemberExpression;
                name = body.Member.Name;
            }
            else
            {
                name = body.Member.Name;
            }

            return name;
        }

    }
}